home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / KillFile.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  10.6 KB  |  427 lines

  1. #import "Alexandra.h"
  2. #import "KillFile.h"
  3. #import "descriptors.h"
  4. #import "Article.h"
  5. #import "NNTP.h"
  6. #import "ArticleSet.h"
  7. #import "ArticleSetMatrix.h"
  8. #import "MainWindowControl.h"
  9. #import "NntpPanelControl.h"
  10. #import <misckit/MiscAppDefaults.h>
  11. #import "plain-subject.h"
  12. #import "MatrixScroller.h"
  13.  
  14. #define MODE_AUTHOR     1
  15. #define MODE_THREAD     0
  16. #define MODE_ASK        2
  17. #define MODE_NOTHING    -1
  18.  
  19. @implementation KillFile
  20.  
  21. - setPath
  22. {
  23.    const char *home=NXHomeDirectory();
  24.    const char *servername=[nntpServer serverName];
  25.  
  26.    path=(char *)malloc((strlen(home)+strlen(servername)+25)*sizeof(char));
  27.    strcpy(path,home);
  28.    strcat(path,"/Library/Alexandra");
  29.    mkdir(path,448);
  30.    strcat(path,"/");
  31.    strcat(path,servername);
  32.    mkdir(path,448);
  33.    strcat(path,"/");
  34.  
  35.    return self;  
  36. }
  37.  
  38. - init
  39. {
  40.    newsgroupTable=[[HashTable alloc] initKeyDesc:"*" valueDesc:"@"];
  41.    authorTable=NULL;
  42.    return self;
  43. }
  44.  
  45. - free
  46. {
  47.    [self saveAndFree];
  48.    
  49.    if(path!=NULL)
  50.       free(path);
  51.  
  52.    return [super free];
  53. }
  54.  
  55. - getAuthorTable
  56. {
  57.    char buf[255],buf2[255];
  58.    FILE *fd;
  59.    char *fullPath;
  60.    char tableFile[]="KILLAUTHORS";
  61.  
  62.    if(authorTable!=NULL)
  63.       return authorTable;
  64.    fullPath=(char *)malloc(strlen(path)+strlen(tableFile)+2);
  65.    strcpy(fullPath,path);
  66.    strcat(fullPath,tableFile);
  67.    authorTable=[[HashTable alloc] initKeyDesc:"*" valueDesc:"i"];
  68.  
  69.    fd=fopen(fullPath,"r");   
  70.    if(fd!=NULL){
  71.       while(fgets(buf,255,fd),((buf[0]!='.')&&(buf[0]!='\0'))){
  72.          if(buf[0]!='A')
  73.             continue;
  74.             *buf2='\0';
  75.          sscanf(buf,"A %[^\n]\n",buf2);
  76.             if(strlen(buf2)>2)
  77.             [authorTable insertKey:(const void *)NXCopyStringBuffer(buf2) value:(void *)1];
  78.       }
  79.    fclose(fd);
  80.    }
  81.  
  82.    free(fullPath);
  83.  
  84.    return authorTable; 
  85. }
  86.  
  87. - getThreadTableFor:(const char *)newsgroup
  88. {
  89.    id groupTable=[newsgroupTable valueForKey:newsgroup];
  90.    char *fullPath;
  91.    FILE *fd;
  92.    char buf[255],buf2[255];
  93.  
  94.    long *s;
  95.    long seconds;
  96.    struct timeval tp;
  97.    struct timezone tzp;
  98.    long dist;
  99.  
  100.    if(newsgroup==NULL){
  101.        NXLogError("KillFile: Newsgroup with NULL pointer as name");
  102.         return groupTable;
  103.     }
  104.     
  105.    gettimeofday(&tp,&tzp);
  106.    dist=[NXApp defaultIntValue:"KillfileExpire"];
  107.    if(dist==0)
  108.       dist=30;
  109.    dist=dist*24*60*60;
  110.  
  111.    if(groupTable!=nil)
  112.       return groupTable;
  113.    groupTable=[[HashTable alloc] initKeyDesc:"*" valueDesc:"!"];
  114.    [newsgroupTable insertKey:NXCopyStringBuffer(newsgroup) value:groupTable];
  115.  
  116.    fullPath=(char *)malloc(strlen(path)+strlen(newsgroup)+2);
  117.    strcpy(fullPath,path);
  118.    strcat(fullPath,newsgroup);
  119.  
  120.    fd=fopen(fullPath,"r");   
  121.    if(fd!=NULL){
  122.       while(fgets(buf,255,fd),((buf[0]!='.')&&(buf[0]!='\0'))){
  123.          if(buf[0]!='S')
  124.             continue;
  125.             *buf2='\0';
  126.          sscanf(buf,"S %[^\t]\t%ld",buf2,&seconds);
  127.          if(strlen(buf2)>0 && tp.tv_sec-seconds<dist){
  128.             s=(long *)malloc(sizeof(long));
  129.             *s=seconds;
  130.             [groupTable insertKey:(const void *)NXCopyStringBuffer(buf2) value:(void *)s];
  131.          }
  132.       }
  133.    fclose(fd);
  134.    }
  135.    free(fullPath);
  136.  
  137.    return groupTable;
  138. }
  139.  
  140. - killUnkillAuthor:(BOOL)doKill author:(const char *)author
  141. {
  142.    id aTable=[self getAuthorTable];
  143.  
  144.    if((author==NULL)||(author[0]=='\0')) return nil;
  145.    if(doKill==TRUE){
  146.       if(![aTable isKey:author]){
  147.          char *name=NXCopyStringBuffer(author);
  148.          [aTable insertKey:(const void *)name value:(void *)1];
  149.       }
  150.    }
  151.    else
  152.       [aTable removeKey:author];
  153.  
  154.    return self;
  155. }
  156.  
  157. - killUnkillThread:(BOOL)doKill :(char *)subject newsgroup:(id)group
  158. {
  159.    char *val;
  160.    id subjectTable=[self getThreadTableFor:[group stringValue]];
  161.    struct timeval tp;
  162.    struct timezone tzp;
  163.    BOOL dummy;
  164.  
  165.    gettimeofday(&tp,&tzp);
  166.  
  167.    if((subject==NULL)||(subject[0]=='\0')) return nil;
  168.    val=plain_subject(subject,&dummy);
  169.    if((val==NULL)||(val[0]=='\0')) return nil;
  170.    if((doKill==TRUE)&&([subjectTable isKey:val]==FALSE)){
  171.       long *s=(long *)malloc(sizeof(long));
  172.       *s=tp.tv_sec;
  173.       [subjectTable insertKey:(const void *)NXCopyStringBuffer(val) value:(void *)s];
  174.    }
  175.    else{
  176.       [subjectTable removeKey:val];
  177.       free(val);
  178.    }
  179.  
  180.    return self;
  181. }
  182.  
  183. - filterArticles:(id)newsgroup andReloadMatrix:(BOOL)reload
  184. {
  185.    int i,j;
  186.    id aList=[newsgroup articleList];
  187.    id subjectTable=[self getThreadTableFor:[newsgroup stringValue]];
  188.    id aTable=[self getAuthorTable];
  189.    int num_unread_killed=0;
  190.    struct timeval tp;
  191.    struct timezone tzp;
  192.    BOOL subjectFits,authorFits;
  193.    BOOL dummy;
  194.    int rememberNextPos=0;
  195.     id nextCell=nil;
  196.     id selectedCell=[[theArticleSet theMatrix] selectedCell];
  197.  
  198.    gettimeofday(&tp,&tzp);
  199.  
  200.    j=[aList count];
  201.    for(i=0;i<j;i++){
  202.       id anArticle=[aList objectAt:i];
  203.       const char *subject,*lsubject;
  204.       const char *name;
  205.         headerDesc *h;
  206.         
  207.       if([anArticle isKilled])
  208.          continue;
  209.         
  210.         h=[anArticle header];
  211.         if(!h){
  212.             NXLogError("KillFile.m: article with NULL pointer header");
  213.             continue;
  214.         }
  215.         
  216.         lsubject=h->fieldBody[SUBJECT];
  217.         if(!lsubject){
  218.            NXLogError("KillFile: Article with NULL pointer as subject");
  219.             continue;
  220.         }
  221.         
  222.       subject=plain_subject((char *)lsubject,&dummy);
  223.       name=[anArticle realName];
  224.       subjectFits=((subject!=NULL)&&([subjectTable isKey:subject]));
  225.       authorFits=((name!=NULL)&&([aTable isKey:name]));
  226.       if( subjectFits || authorFits){
  227.          if([anArticle isRead]==FALSE)
  228.             num_unread_killed++;
  229.          [anArticle kill];
  230.          if(subjectFits)
  231.             *(long *)[subjectTable valueForKey:subject]=tp.tv_sec;
  232.             if((rememberNextPos==0)&&(selectedCell==anArticle))
  233.                rememberNextPos=1;
  234.       }
  235.        else
  236.            if((rememberNextPos==1)&&([anArticle isTaged])){
  237.                rememberNextPos=2;
  238.                 nextCell=anArticle;
  239.             }
  240.                
  241.    }
  242.    if(reload==TRUE){
  243.       id matrix=[theArticleSet theMatrix];
  244.       [matrix reloadMatrix];
  245.       [matrix display];
  246.       [theArticleSet sync];
  247.       if(num_unread_killed>0){
  248.          [newsgroup incNumberUnreadArticles:-1*num_unread_killed];
  249.          [[theNGSet theMatrix] display];
  250.       }
  251.         if(nextCell!=nil){
  252.            int row,col;
  253.             [matrix getRow:&row andCol:&col ofCell:nextCell];
  254.            [matrix selectCellAt:row :col];
  255.          [theArticleSet selectArticle:self];
  256.          [matrix scrollCellToVisible:row upperOffset:1.5 lowerOffset:1.5];
  257.       }
  258.    }
  259.  
  260.    return self;
  261. }
  262.  
  263. - saveAndFree
  264. {
  265.    FILE *fd;
  266.    char *fullPath;
  267.    char tableFile[]="KILLAUTHORS";
  268.    BOOL success=TRUE;
  269.  
  270.    if(authorTable!=nil){
  271.       fullPath=(char *)malloc(strlen(path)+strlen(tableFile)+2);
  272.       strcpy(fullPath,path);
  273.       strcat(fullPath,tableFile);
  274.  
  275.       if([authorTable count]>0){
  276.          fd=fopen(fullPath,"w");
  277.          if(fd==NULL)
  278.             success=FALSE;
  279.          else{
  280.             char *name; 
  281.             int *value; 
  282.             NXHashState state=[authorTable initState]; 
  283.             while([authorTable nextState:&state key:(const void **)&name value:(void **)&value]){
  284.                fprintf(fd,"A %s\n",name);
  285.                free(name);
  286.             }
  287.             fprintf(fd,".\n");
  288.             fclose(fd);
  289.          }
  290.          free(fullPath);
  291.       }
  292.       else
  293.          remove(fullPath);
  294.       free(fullPath);
  295.       [authorTable free];
  296.    }
  297.  
  298.    if([newsgroupTable count]>0){
  299.       char *gname;
  300.       id groupTable; 
  301.       NXHashState ngstate=[newsgroupTable initState]; 
  302.       while([newsgroupTable nextState:&ngstate key:(const void **)&gname value:(void **)&groupTable]){
  303.          fullPath=(char *)malloc(strlen(path)+strlen(gname)+2);
  304.          strcpy(fullPath,path);
  305.          strcat(fullPath,gname);
  306.  
  307.          if([groupTable count]>0){
  308.             fd=fopen(fullPath,"w");
  309.             if(fd==NULL)
  310.                success=FALSE;
  311.             else{
  312.                char *subject; 
  313.                long *value; 
  314.                NXHashState gstate=[groupTable initState]; 
  315.                while([groupTable nextState:&gstate key:(const void **)&subject value:(void **)&value]){
  316.                   fprintf(fd,"S %s\t%ld\n",subject,*value);
  317.                   free(subject);
  318.                   free(value);
  319.                }
  320.                fprintf(fd,".\n");
  321.                fclose(fd);
  322.             }
  323.          }
  324.          else
  325.             remove(fullPath);
  326.          free(fullPath);
  327.          [groupTable free];
  328.       }
  329.    }
  330.  
  331.    [newsgroupTable free];
  332.    if(success==FALSE)
  333.       NXLogError("Can't save killfile.\n");
  334.  
  335.    return self;
  336. }
  337.  
  338. - killAuthor:sender
  339. {
  340.    [self killSelectionMode:MODE_AUTHOR];
  341.    return self;
  342. }
  343.  
  344. - killThread:sender
  345. {
  346.     
  347.    if([theArticleSet currentSelection]==nil){
  348.       NXRunAlertPanel("ALEXANDRA","No article selected.",NULL,NULL,NULL);
  349.       return nil;
  350.    }
  351.    if(![NXApp defaultBoolValue:"DoNotConfirmAKill"])
  352.       if(NXRunAlertPanel("ALEXANDRA","Do you really want to kill this thread?",NULL,"Cancel",NULL)==NX_ALERTALTERNATE)
  353.          return self;
  354.  
  355.    [self killSelectionMode:MODE_THREAD];
  356.     
  357.    return self;
  358. }
  359.  
  360.  
  361. #define ALERT_KILLWHAT NXLocalString("Do you want to kill all articles of this thread or all articles written by this author?",NULL,"Frage was ge-kill-t werden soll.")
  362.  
  363. #define ALERT_NOARTICLE NXLocalString("You must select an Article first.",NULL,"Alert Panel falls Kill aufgerufen wird ohne daß ein Artikel selektiert ist.")
  364.  
  365. #define STRG_CANCEL NXLocalString("Cancel",NULL,"In Alert-Panels")
  366. #define STRG_AUTHOR NXLocalString("Author",NULL,"In Kill-Alert-Panel")
  367. #define STRG_THREAD NXLocalString("Thread",NULL,"In Kill-Alert-Panel")
  368.  
  369.  
  370. - kill:sender;
  371.     {
  372.     int killMode,ret;
  373.     
  374.     if([theArticleSet currentSelection]==nil)
  375.         {
  376.         NXRunAlertPanel("ALEXANDRA",ALERT_NOARTICLE,STRG_CANCEL,NULL,NULL);
  377.         return nil;
  378.         }
  379.     killMode=[NXApp defaultIntValue:DEFAULT_KILL_BEHAVIOUR];
  380.     if(killMode==MODE_ASK)
  381.         {
  382.         ret=NXRunAlertPanel("ALEXANDRA", ALERT_KILLWHAT, STRG_THREAD,
  383.                     STRG_AUTHOR, STRG_CANCEL);
  384.         if(ret==NX_ALERTDEFAULT)
  385.             killMode=MODE_THREAD;
  386.         else if(ret==NX_ALERTALTERNATE)
  387.             killMode=MODE_AUTHOR;
  388.         else
  389.             killMode=MODE_NOTHING;
  390.         }
  391.     if(killMode!=MODE_NOTHING)
  392.         [self killSelectionMode:killMode];
  393.     return self;
  394.     }
  395.  
  396.  
  397. - killSelectionMode:(int)mode
  398. {
  399.    id currentNewsgroup,currentArticle;
  400.    char *subject;
  401.     
  402.    if((currentNewsgroup=[theNGSet currentSelection])==nil)
  403.       return nil;
  404.    if((currentArticle=[theArticleSet currentSelection])==nil)
  405.       return nil;
  406.  
  407.    if(mode==MODE_THREAD){
  408.       subject=[currentArticle header]->fieldBody[SUBJECT];
  409.       
  410.       if((subject==NULL)||(subject[0]=='\0')) 
  411.          return nil;
  412.       [self killUnkillThread:YES :subject newsgroup:currentNewsgroup];
  413.       [self filterArticles:currentNewsgroup andReloadMatrix:YES];
  414.    }
  415.    else{
  416.       const char *name=[currentArticle realName];
  417.       if((name==NULL)||(name[0]=='\0'))
  418.          return nil;
  419.       [self killUnkillAuthor:YES author:name];
  420.       [self filterArticles:currentNewsgroup andReloadMatrix:YES];
  421.    }
  422.  
  423.    return self;
  424. }
  425.  
  426. @end
  427.